package mathy.wili.c;
import java.io.File;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.security.CodeSource;
import java.sql.Clob;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Consumer;
import mathy.c.Ca;
import mathy.wili.autoeq.Refs;
import mathy.wili.extract_srcFile.java.Coms26;
/**
 * Contribute some function relevant to Class.
 * 
 * @see {@link Class9_testCase}
 */
@SuppressWarnings("all")
public class Class9 {
	/**
	 * @param name
	 *            , full class type name or simple class name.<br>
	 *            e.g, int[] <br>
	 *            e.g, java.lang.String[][].
	 * @throws ClassNotFoundException
	 */
	public static Class<?> arrayClass_ofName(String name) {
		int arr = name.indexOf('[');
		String name1 = arr == -1 ? name : name.substring(0, arr);
		Class<?> cls = classOfName(name1, -1);
		if (arr == -1 || cls == null)
			return cls;
		int dim = 0;
		dim = (name.length() - arr) / 2;
		return arrryClassOf(cls, dim);
	}

	/**
	 * 根据类名，返回简单类 | java.lang包下类 | 常用类, 若是全名则返回全名类
	 * 类名后可根数组。eg. "int[]" has return int[].class.
	 * @throws ClassNotFoundException
	 */
	public static Class<?> classOfName(String name, int showError) {
		if (name.endsWith("]")) {
			return arrayClass_ofName(name);
		}
		switch (name) {//比用 forName("java.lang." + name, -1)快2，3倍。但都很快
		case "byte"://原始名
			return byte.class;
		case "Byte"://简单名
		case "java.lang.Byte"://全类名
			return Byte.class;
		case "short":
			return short.class;
		case "Short":
		case "java.lang.Short":
			return Short.class;
		case "char":
			return char.class;
		case "Character":
		case "java.lang.Character":
			return Character.class;
		case "int":
			return int.class;
		case "Integer":
		case "java.lang.Integer":
			return Integer.class;
		case "long":
			return long.class;
		case "Long":
		case "java.lang.Long":
			return Long.class;
		case "float":
			return float.class;
		case "Float":
		case "java.lang.Float":
			return Float.class;
		case "double":
			return double.class;
		case "Double":
		case "java.lang.Double":
			return Double.class;
		//
		case "boolean":
			return boolean.class;
		case "Boolean":
		case "java.lang.Boolean":
			return Boolean.class;
		//===========================
		case "String":
		case "java.lang.String":
			return String.class;
		//===========================util包需要全类名
		case "java.util.Collection":
			return Collection.class;
		case "java.util.List":
			return List.class;
		case "java.util.ArrayList":
			return ArrayList.class;
		case "java.util.Set":
			return Set.class;
		case "java.util.HashSet":
			return HashSet.class;
		case "java.util.TreeSet":
			return TreeSet.class;
		case "java.util.Map":
			return Map.class;
		case "java.util.HashMap":
			return HashMap.class;
		case "java.util.TreeMap":
			return TreeMap.class;
		case "java.util.Date":
			return Date.class;
		case "java.sql.Date":
			return java.sql.Date.class;
		case "java.sql.Timestamp":
			return java.sql.Timestamp.class;
		case "java.math.BigDecimal": {
			return java.math.BigDecimal.class;
		}
		case "java.lang.Object":
			return Object.class;
		case "java.util.function.Consumer":
			return java.util.function.Consumer.class;
		case "java.util.function.Function":
			return java.util.function.Function.class;
		case "java.util.function.Predicate":
			return java.util.function.Predicate.class;
		case "java.util.function.UnaryOperator":
			return java.util.function.UnaryOperator.class;
		default:
			if (name.contains("."))
				return Class9.forName(name, showError);
			Class ret = Class9.forName("java.lang." + name, -1);
			if (ret != null)
				return ret;
			return null;
		}
	}
	static ClassLoader classLoader;

	static final Map<String, Class<?>> CLS_MAP = new HashMap<>();
	/**
	 * try to invoke Class.forName
	 */
	public static Class<?> forName(String className, int showErr) {
		if (classLoader == null) {
			classLoader = ClassLoader.getSystemClassLoader();
		}
		try {
			if (className.equals("T"))
				C2.pause();
			Class cls = CLS_MAP.get(className);
			if (cls == null) {
				if ("456".isEmpty()) {
					cls = Class.forName(className);//fully qualified name 
				} else {
					//不执行静态块，Class.forName()则会执行, 但可以在静态内容中构造实例,....
					if (className.charAt(0) == '[') {
						cls = Class.forName(className);//eg. [Ljava.lang.Object; return Object[].class
					} else {
						cls = classLoader.loadClass(className);//binary name
					}
				}
				if (CLS_MAP.size() > 10000) {
					CLS_MAP.clear();
					String st = "\n" + "ERROR: Class9.forName:CLS_MAP.size=" + CLS_MAP.size();
					Ca.info(1, st);
				}
				CLS_MAP.put(className, cls);
			}
			return cls;
		} catch (ClassNotFoundException e) {
			if (showErr != 99) {
				int ind = className.lastIndexOf('.');
				if (ind != -1) {
					//bug: 内部类的内部类需，再来一个'$'
					className = className.substring(0, ind) + '$' + className.substring(ind + 1);
					Class<?> ret = forName(className, 99);
					if (ret != null)
						return ret;
					if (showErr == 1)
						e.printStackTrace();
					else if (showErr > 1)
						throw new RuntimeException(e);
				}
			}
			return null;
		}
	}

	public static Method getDeclaredMethod(int throwError, Class<?> cls, String method, Class... types) {
		try {
			Method[] md = cls.getDeclaredMethods();
			return cls.getDeclaredMethod(method, types);
		} catch (Exception e) {
			if (throwError < 0)
				return null;
			++inc;
			System.err
					.println(inc + ",Class9.getDeclaredMethod:" + cls.getName() + "." + method + Arrays.asList(types));
			if (throwError == 1) {
				e.printStackTrace();
				return null;
			}
			throw new RuntimeException(e);
		}
	}

	public static Class<?> bottomTypeOf(Class cls) {
		if (cls.isArray())
			return bottomTypeOf(cls.getComponentType());
		return cls;
	}

	public static void setFieldValue(String fieldName, Object fieldValue, Object obj) {
		if (fieldValue instanceof Clob) {
		}
		Field fd = fieldOf2(fieldName, obj.getClass());
		fd.setAccessible(true);
		try {
			if (fieldValue instanceof Timestamp) {//Expedient
				if (java.util.Date.class.isAssignableFrom(fd.getType())) {
					//					Timestamp t = (Timestamp) fieldValue;
					//					fieldValue = new java.sql.Date(t.getTime());
				}
			}
			fd.set(obj, fieldValue);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	public static void findClassUpwards(Class<?> cls, Consumer<Class<?>> consu) {
		//eg. Arrays.asList()若传入两个数字，如何能灵活转换成 Object[].class
		consu.accept(cls);
		Method ret;
		if (cls.getSuperclass() != null) {
			findClassUpwards(cls.getSuperclass(), consu);
		}
		Class[] cc = cls.getInterfaces();
		for (Class ele : cc) {
			findClassUpwards(ele, consu);
		}
	}

	public static Method findMethodUpwards(int showError, Class<?> cls, String name, Class<?>... pmTypes) {
		//eg. Arrays.asList()若传入两个数字，如何能灵活转换成 Object[].class
		Exception ex = null;
		Method ret;
		{
			try {
				return cls.getDeclaredMethod(name, pmTypes);
			} catch (Exception e) {
				if (ex == null)
					ex = e;
				if (cls.getSuperclass() != null) {
					ret = findMethodUpwards(-1, cls.getSuperclass(), name, pmTypes);
					if (ret != null)
						return ret;
				}
				Class[] cc = cls.getInterfaces();
				for (Class ele : cc) {
					ret = findMethodUpwards(-1, ele, name, pmTypes);
					if (ret != null)
						return ret;//eg. cls is HttpServletRequest
				}
			}
		}
		Ca.pause();
		if (showError == 1)
			ex.printStackTrace();
		else if (showError == 2)
			throw new RuntimeException(ex);
		return null;
	}

	public static Field findFieldUpwards(int showError, Class cls, String fieldName) {
		Field ret;
		if (cls.isArray() && fieldName.equals("length"))
			return null;
		Exception ex = null;
		try {
			return cls.getDeclaredField(fieldName);
		} catch (Exception e) {
			if (ex == null)
				ex = e;
			if (cls.getSuperclass() != null) {
				ret = findFieldUpwards(-1, cls.getSuperclass(), fieldName);
				if (ret != null)
					return ret;
			}
			Class[] cc = cls.getInterfaces();
			for (Class ele : cc) {
				ret = findFieldUpwards(-1, ele, fieldName);
				if (ret != null)
					return ret;//eg. cls is HttpServletRequest
			}
		}
		if (showError == 1)
			ex.printStackTrace();
		else if (showError == 2)
			throw new RuntimeException(ex);
		return null;
	}

	public static Field fieldOf2(String fname, Class<?> cls) {
		Field ret = null;
		try {
			ret = cls.getDeclaredField(fname);
		} catch (SecurityException e) {
			throw new RuntimeException(e);
		} catch (NoSuchFieldException e) {
			try {
				ret = cls.getField(fname);
			} catch (NoSuchFieldException e1) {
				// seemed redundant.
				Class scls = cls.getSuperclass();
				if (scls != null) {
					ret = fieldOf2(fname, scls);// 
				} else {
					//System.err.println(" fieldOf(),cls=" + cls.getSimpleName());
					throw new RuntimeException(e1);
				}
			}
		}
		if (ret != null) {
			ret.setAccessible(true);// so can visit private field
		}
		return ret;
	}

	public static Field getDeclaredField(String fname, Class c) {
		try {
			return c.getDeclaredField(fname);
		} catch (Exception e) {
			return null;
		}
	}

	public static boolean isAbstractMethod(Method md) {
		int mod = md.getModifiers();
		return Modifier.isAbstract(mod);
	}

	/**
	 * @param cls
	 * @return return true if class is interface or abstract class.
	 */
	public static boolean isAbstractOrInterface(Class<?> cls) {
		if (cls.isPrimitive() || cls.isArray())
			return false;
		int mod = cls.getModifiers();
		return Modifier.isInterface(mod) || Modifier.isAbstract(mod);
	}

	/**
	 * ...反射当支持这类转型。
	 */
	/**
	 * 	.勉强可做id, 但类用的是简单名。
	 */
	public static String strOfMethod(Member mem) {
		if (mem instanceof Executable) {
			Executable ee = (Executable) mem;
			String st = Class9.strOfMethod(mem.getName(), ee.getParameterTypes());
			if (ee instanceof Method) {
				Method md = (Method) ee;
				//md.getReturnType().getSimpleName() 
				return mem.getDeclaringClass().getSimpleName() + "." + st;
			} else {
				return st;
			}
		} else if (mem instanceof Field) {
			Field fd = (Field) mem;
			return fd.getType().getSimpleName() + " " + fd.getDeclaringClass().getSimpleName() + "." + fd.getName();
		} else {
			return mem.toString();
		}
	}

	public static String strOfMethod(String name, Class<?>... cc) {
		StringBuilder sb = new StringBuilder();
		sb.append(name);
		sb.append('(');
		for (int i = 0; i < cc.length; i++) {
			Class cls = cc[i];
			if (i > 0)
				sb.append(',');
			sb.append(cls.getSimpleName());//未能显示数组类的串形式?
		}
		sb.append(')');
		return sb.toString();
	}

	public static Boolean isStaticMethod(Method md) {
		return Modifier.isStatic(md.getModifiers());
	}

	public static Boolean isStaticField(Field fd) {
		return Modifier.isStatic(fd.getModifiers());
	}

	/**
	 * int[][][].class==arrayClassOf(int.class, 3)<br>
	 */
	public static Class<?> arrryClassOf(Class<?> buttomType, int dimension) {
		if (dimension == 0) {
			return null;
		}
		Object obj = Array.newInstance(buttomType, new int[dimension]);
		return obj.getClass();
	}

	public static URL jarURL_orClassRootURL(Class<?> cls) {
		CodeSource src = cls.getProtectionDomain().getCodeSource();
		return src == null ? null : src.getLocation();
	}

	/**
	 * @return  $cls所在的jar文件，或者 $cls所在的类的根目录。
	 */
	public static File jarFileOrClassRootOf(Class<?> cls) {//速度慢
		URL url = jarURL_orClassRootURL(cls);
		try {
			return url == null ? null : new File(url.toURI());
		} catch (Exception e) {
			//throw new RuntimeException(e);
			return null;
		}
	}

	/**
	 *  Return one abstract method, if given method implementing it.
	 */
	public static Method getAbstractMethodOf(Method method) {//@Override
		if (Boolean.FALSE) {
			method.getAnnotation(Override.class);
			Class9.implementMethodsOf(null);
		}
		if (method == null)
			return null;
		Class<?> cls = method.getDeclaringClass();
		//System.out.println("getAbstractMethodOf:" + cls);
		if (Class9.class.isAssignableFrom(cls) && Coms26.ref(3))
			Ca.pause();
		Class superCls = cls.getSuperclass();
		Class[] pp = method.getParameterTypes();
		while (superCls != null) {
			try {
				Method ret = superCls.getDeclaredMethod(method.getName(), pp);
				if (Modifier.isAbstract(ret.getModifiers()))
					return ret;
				ret = getAbstractMethodOf(ret);
				if (ret != null)
					return ret;
			} catch (NoSuchMethodException | SecurityException e) {
			}
			superCls = superCls.getSuperclass();
		}
		Class[] supers = cls.getInterfaces();
		List<Class> superList = new ArrayList<>(supers.length * 2);
		for (Class su : supers)
			superList.add(su);
		f1: for (int i = 0; i < superList.size(); i++) {
			Class<?> face = superList.get(i);
			while (face != null) {
				try {
					return face.getDeclaredMethod(method.getName(), pp);
				} catch (NoSuchMethodException | SecurityException e) {
					Class[] cc = face.getInterfaces();
					for (Class c : cc)
						superList.add(c);
				}
				continue f1;
			}
		}
		return null;
	}

	/**
	 * 	取得类下的实现方法（方法实现或覆写了超类的接口或方法）
	 * @see Class9#getAbstractMethodOf(Method)
	 */
	public static List<Method> implementMethodsOf(Class<?> cls) {
		if (Boolean.FALSE) {
			getAbstractMethodOf(null);//和它有重复逻辑
		}
		if (isAbstractOrInterface(cls))
			return null;
		List<Method> methosList = null;
		Class<?> supCls = cls.getSuperclass();
		Method[] mm = cls.getDeclaredMethods();
		f1: for (int i = 0; i < mm.length; i++) {
			Method curMethod = mm[i];
			Class[] pp = curMethod.getParameterTypes();
			if (supCls != null) {
				Method md2 = Class9.findMethodUpwards(-1, supCls, curMethod.getName(), pp);
				if (md2 != null) {
					if (methosList == null)
						methosList = new ArrayList<>();
					methosList.add(md2);
					continue f1;
				}
			}
			for (Class<?> cc : cls.getInterfaces()) {
				Method md2 = Class9.findMethodUpwards(-1, cc, curMethod.getName(), pp);
				if (md2 != null) {
					if (methosList == null)
						methosList = new ArrayList<>();
					methosList.add(md2);
					continue f1;
				}
			}
		}
		return methosList;
	}

	public static Class<?> rootClsOf(Class cls) {
		if (cls.isMemberClass())
			return rootClsOf(cls.getDeclaringClass());
		return cls;
	}

	public static boolean isStaticFinalSimpleValue(Class<?> cls, Field fd) {
		//提示：jdk仅对严格的 静态常量简单值进行值替换（使其不存在于字节码中，asm找不到）
		//eg. 对于 static final Object a=23, return false.  对于 static int a=23, return true;
		int mod = fd.getModifiers();
		if (Boolean.TRUE || Modifier.isPublic(mod)) {
			if (Modifier.isStatic(mod) && Modifier.isFinal(mod)) {
				Class<?> type = fd.getType();
				if (type.isPrimitive() || type.equals(String.class)) {
					return true;
				} else {
					//this field must in bytecode
				}
			}
		}
		return false;
	}
	public static class ClsAndName implements Serializable {
		private static final long serialVersionUID = 1L;

		public Class<?> cls;

		public String name;
		public String toString() {
			return cls.getSimpleName() + "." + name;
		}

		public ClsAndName(Class<?> cls, String name) {
			this.cls = cls;
			this.name = name;
		}
	}
	public static Class classOfClassFile(String packageName, File file) {
		if (!file.getName().endsWith(".class"))
			return null;
		String fname = file.getName();
		fname = fname.substring(0, fname.lastIndexOf('.'));
		return Class9.forName(packageName + "." + fname, 1);
	}

	public static Class[] getAllClass_inPackage(Class<?> cls) {
		String pname = cls.getPackageName();
		File file = File9.clsFileOfClass(cls, null);
		File[] ff = file.getParentFile().listFiles((File fil) -> {
			String fname = fil.getName();
			int ind = fname.lastIndexOf('$');
			if (ind != -1 && Str9.isNumChar(fname, ind + 1))
				return false;
			return fname.endsWith(".class");
		});
		Class[] cc = new Class[ff.length];
		for (int i = 0; i < cc.length; i++) {
			cc[i] = classOfClassFile(pname, ff[i]);
			if (cc[i] == null) {
				Ca.pause();
				continue;
			}
		}
		return cc;
	}

	/**
	 * 	类所在源码文件的首个类。
	 */
	public static Class<?> fileClassOf(Class cls) {
		if (cls.isMemberClass())
			return fileClassOf(cls.getDeclaringClass());
		if (cls.isAnonymousClass())//eg. wili.c.asm.Demo110$1
			return fileClassOf(cls.getEnclosingClass());
		if (cls.isLocalClass())
			return fileClassOf(cls.getDeclaringClass());//right?
		if (cls.isSynthetic()) {
			Refs.ref();//right?
			return fileClassOf(cls.getDeclaringClass());
		}
		return cls;
	}

	public static String packageNameOfClassFile(String rootPath, File file) {
		String pkgNam = file.getAbsolutePath();
		pkgNam = pkgNam.substring(0, pkgNam.lastIndexOf(File.separatorChar));
		pkgNam = pkgNam.replace(File.separatorChar, '.');
		pkgNam = pkgNam.substring(rootPath.length() + 1);
		return pkgNam;
	}

	public static boolean isRootClass(Class cls) {
		if (cls.isMemberClass() || cls.isSynthetic() || cls.isLocalClass() || cls.isAnonymousClass())
			return false;
		return true;
	}
	private static int inc, incStep, incStep2, incStep3;
}